package com.example.demo.runner.code;

import com.example.demo.entity.User;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

public abstract class AbstractCodeRunner implements CodeRunnerInterface {
    protected final BlockingQueue<RunnerMessage> inputQueue;
    protected final BlockingQueue<RunnerMessage> outputQueue;
    private final Executor executor = Executors.newFixedThreadPool(2);
    protected InputStream subProcStdOutStream;
    protected InputStream subProcStdErrStream;
    protected OutputStream subProcStdInStream;
    private User user;

    AbstractCodeRunner(User user) {
        this.inputQueue = new LinkedBlockingQueue<>();
        this.outputQueue = new LinkedBlockingQueue<>();
        this.user = user;
    }

    public User getUser() {
        return user;
    }

    @Override
    public void run() {
        RunnerMessage input;
        executor.execute(new StreamReader(subProcStdOutStream, outputQueue));
        // TODO error queue
        try {
            input = inputQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
            stop();
            return;
        }

        while (input != RunnerMessage.POISON_PILL) {
            try {
                BufferedWriter writer =
                    new BufferedWriter(new OutputStreamWriter(subProcStdInStream));
                writer.write(input.getStrMsg());
                writer.flush();
            } catch (IOException e) {
                // TODO check exceprtion
                e.printStackTrace();
                break;
            }
            try {
                input = inputQueue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
        stop();
        System.out.println("code runner stop: " + this);
    }

    public void stop() {
        try {
            inputQueue.put(RunnerMessage.POISON_PILL);
            outputQueue.put(RunnerMessage.POISON_PILL);
            subProcStdInStream.close();
            subProcStdOutStream.close();
            subProcStdErrStream.close();

        } catch (InterruptedException | IOException ignored) {
        }
    }

    public BlockingQueue<RunnerMessage> getInputQueue() {
        return inputQueue;
    }

    public BlockingQueue<RunnerMessage> getOutputQueue() {
        return outputQueue;
    }
}
